#ifndef SEQIOT_APDU_H
#define SEQIOT_APDU_H

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include "model.h"

typedef struct _apdu_head_fixed_t {
    uint8_t bof;
}__attribute__((packed)) apdu_head_fixed_t;

#define APDU_BOF_FIX (0x10)

typedef struct _apdu_head_variable_t {
    uint8_t bof;
    uint8_t len, len1;
    uint8_t bof1;
}__attribute__((packed)) apdu_head_variable_t;

#define APDU_BOF_VAR (0x68)

typedef struct _apdu_tail_t {
    uint8_t cs;
    uint8_t eof;
}__attribute__((packed)) apdu_tail_t;

#define APDU_EOF (0x16)

typedef enum _func_code_e {
    fc_reset_link = 0,
    fc_reset_process = 1,
    fc_req_data_ack = 3,
    fc_req_data_no_ack = 4,
    fc_req_data_reset_fcb = 7,
    fc_req_access = 8,
    fc_req_link_status = 9,
    fc_req_data_1 = 10,
    fc_req_data_2 = 11,

    fc_ack = 0,
    fc_n_ack = 1,
    fc_resp_data = 8,
    fc_resp_no_data = 9,
    fc_resp_link_status = 11
} func_code_e;

typedef enum _primary_e {
    primary, secondary
} primary_e;

typedef enum _direction_t {
    control, monitor
} direction_t;

typedef struct _apdu_ctrl_t {
    union {
        uint8_t _val;
        struct {
            func_code_e fun: 4;
            _Bool fcv: 1;//dfc
            _Bool fcb: 1;//acd
            primary_e prm: 1;
            direction_t dir: 1;
        };
    };
}__attribute__((packed)) apdu_ctrl_t;

typedef struct _apdu_body_t {
    apdu_ctrl_t ctl;
    uint8_t addr;
}__attribute__((packed)) apdu_body_t;

typedef struct _apdu_fixed_t {
    apdu_head_fixed_t head;
    apdu_body_t body;
    apdu_tail_t tail;
}__attribute__((packed)) apdu_fixed_t;

typedef struct _apdu_variable_t {
    apdu_head_variable_t head;
    apdu_body_t body;
    uint8_t data[256 - 2/*body*/ - 2/*tail*/];
    apdu_tail_t *p_tail;
}__attribute__((packed)) apdu_variable_t;

typedef struct _apdu_t {
    union {
        uint8_t bof;
        apdu_fixed_t fix;
        apdu_variable_t var;
    }__attribute__((packed));
}__attribute__((packed)) apdu_t;

int apdu_size(const apdu_t *apdu);

_Bool apdu_check_cs_fixed(const apdu_fixed_t *apdu);

_Bool apdu_check_cs_variable(const apdu_variable_t *apdu, const uint8_t *data, int len);

_Bool apdu_check_fixed(const apdu_fixed_t *apdu);

_Bool apdu_check_variable(apdu_variable_t *apdu);

ssize_t apdu_read(channel_t *channel, apdu_t *apdu);

ssize_t apdu_write(channel_t *channel, const apdu_t *apdu);

#endif //SEQIOT_APDU_H
